package cn.zhxu.toys.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 日期工具类
 * 
 * @author Troy.Zhou
 * @since 0.3.3
 */
public class DateUtils {
	
	
	static final Map<String, ThreadLocal<DateFormat>> POOL = new ConcurrentHashMap<>();
	static final ThreadLocal<Calendar> CALENDAR_TL = new ThreadLocal<>();
	
	public static final String YEAR_PATTERN = "yyyy";
	public static final String MONTH_PATTERN = "yyyy-MM";
	public static final String DATE_PATTERN = "yyyy-MM-dd";
	public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm";
	public static final String MILLS_PATTERN = "yyyyMMddHHmmssSSS";
	
	
    /**
     * 格式化日期
     */
    public static String format(Date date, String pattern) {
    	return getFormat(pattern).format(date);
    }
    
    /**
     * 解析日期
     */
    public static Date parse(String date, String pattern) {
    	try {
			return getFormat(pattern).parse(date);
		} catch (ParseException e) {
			throw new RuntimeException("无法解析的日期模式[pattern: " + pattern + ", date: " + date, e);
		}
    }
	
    /**
	 * 按 yyyy 格式化日期
	 */
    public static String formatYear(Date date) {
    	return format(date, YEAR_PATTERN);
    }
    
    /**
	 * 按 yyyy 解析日期
	 */
    public static Date parseYear(String date) {
    	return parse(date, YEAR_PATTERN);
    }
    
    /**
	 * 按 yyyy-MM 格式化日期
	 */
    public static String formatMonth(Date date) {
    	return format(date, MONTH_PATTERN);
    }
    
    /**
	 * 按 yyyy-MM 解析日期
	 */
    public static Date parseMonth(String date) {
    	return parse(date, MONTH_PATTERN);
    }
    
	/**
	 * 按 yyyy-MM-dd 格式化日期
	 */
    public static String formatDate(Date date) {
        return format(date, DATE_PATTERN);
    }

	/**
	 * 按 yyyy-MM-dd 解析日期
	 */
    public static Date parseDate(String date) {
    	return parse(date, DATE_PATTERN);
    }

    /**
	 * 按 yyyy-MM-dd HH:mm 格式化日期
	 */
    public static String formatDateTime(Date date) {
    	return format(date, DATETIME_PATTERN);
    }

    /**
	 * 按 yyyy-MM-dd HH:mm 解析日期
	 */
    public static Date parseDateTime(String date) {
    	return parse(date, DATETIME_PATTERN);
    }

    
    /**
	 * 按 yyyyMMddHHmmssSSS 格式化日期
	 */
    public static String formatMills(Date date) {
    	return format(date, MILLS_PATTERN);
    }
    
    /**
	 * 按 yyyyMMddHHmmssSSS 解析日期
	 */
    public static Date parseMills(String date) {
    	return parse(date, MILLS_PATTERN);
    }
    
    /**
     * 日期向上取整（下一天刚开始）
     */
    public static Date dateCeil(Date date) {
    	Calendar calendar = getCalendar();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * 日期向下取整（当天刚开始）
     */
    public static Date dateFloor(Date date) {
    	Calendar calendar = getCalendar();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * 日期增加
     * @param date 被增加日期
     * @param years 增加的年数
     * @param months 增加的月数
     * @param days 增加的天数
     * @return
     */
    public static Date dateAdd(Date date, int years, int months, int days) {
    	Calendar calendar = getCalendar();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, years);
        calendar.add(Calendar.MONTH, months);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }
    
    /**
     * years 年以后
     */
    public static Date addYears(Date date, int years) {
    	return dateAdd(date, years, 0, 0);
    }
    
    /**
     * months 月以后
     */
    public static Date addMonths(Date date, int months) {
    	return dateAdd(date, 0, months, 0);
    }
    
    /**
     * days 天以后
     */
    public static Date addDays(Date date, int days) {
    	return dateAdd(date, 0, 0, days);
    }
    
    /**
     * 下一天
     */
    public static Date nextDay(Date date) {
        return addDays(date, 1);
    }
    
    /**
     * 前一天
     */
    public static Date prevDay(Date date) {
    	return addDays(date, -1);
    }
    
    /**
     * 日期增加
     * @param date 被增加日期
     * @param hours 添加的小时数
     * @param minutes 添加的分钟数
     * @param seconds 添加的秒数
     * @param millis 添加的毫秒数
     * @return Date
     */
    public static Date timeAdd(Date date, int hours, int minutes, int seconds, int millis) {
    	Calendar calendar = getCalendar();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, hours);
        calendar.add(Calendar.MINUTE, minutes);
        calendar.add(Calendar.SECOND, seconds);
        calendar.add(Calendar.MILLISECOND, millis);
        return calendar.getTime();
    }
    
    /**
     * hours 小时以后
     */
    public static Date addHours(Date date, int hours) {
    	return timeAdd(date, hours, 0, 0, 0);
    }
    
    /**
     * minutes 分钟以后
     */
    public static Date addMinutes(Date date, int minutes) {
    	return timeAdd(date, 0, minutes, 0, 0);
    }
    
    /**
     * seconds 秒以后
     */
    public static Date addSeconds(Date date, int seconds) {
    	return timeAdd(date, 0, 0, seconds, 0);
    }
    
    /**
     * 获取某月的天数
     * @param month 月份（yyyy-MM）
     * @return 天数
     */
    public static int daysOfMonth(String month) {
        return daysOfMonth(parseMonth(month));
    }
    
    /**
     * 获取某月的天数
     * @param month 月份
     * @return 天数
     */
    public static int daysOfMonth(Date month) {
        Calendar calendar = getCalendar();
        calendar.setTime(month);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }
    
    /**
     * 获取某年的天数
     * @param year 年（yyyy）
     * @return 天数
     */
    public static int daysOfYear(String year) {
        return daysOfYear(parseYear(year));
    }
    
    /**
     * 获取某年的天数
     * @param year 年
     * @return 天数
     */
    public static int daysOfYear(Date year) {
        Calendar calendar = getCalendar();
        calendar.setTime(year);
        return calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
    }
    
    /**
     * 获取今天刚开始的时刻
     * @return
     */
    public static Date todayBeginning() {
    	Calendar calendar = getCalendar();
    	calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * 是否是月初
     */
    public static boolean isMonthBeginning(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_MONTH) == 1;
    }

    /**
     * 是否是月底
     */
    public static boolean isMonthEnding(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, 1);
        return cal.get(Calendar.DAY_OF_MONTH) == 1;
    }

    /**
     * 是否是年初
     */
    public static boolean isYearBeginning(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_YEAR) == 1;
    }

    /**
     * 是否是年底
     */
    public static boolean isYearEnding(Date date) {
        Calendar cal = getCalendar();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_YEAR, 1);
        return cal.get(Calendar.DAY_OF_YEAR) == 1;
    }
    
    /**
     * 最小日期
     */
    public static Date min(Date... dates) {
    	if (dates.length == 0) {
    		return null;
    	}
    	Date m = dates[0];
    	for (int i = 1; i < dates.length; i++) {
    		if (m.getTime() > dates[i].getTime()) {
    			m = dates[i];
    		}
    	}
        return m;
    }
    
    /**
     * 最大日期
     */
    public static Date max(Date... dates) {
    	if (dates.length == 0) {
    		return null;
    	}
    	Date m = dates[0];
    	for (int i = 1; i < dates.length; i++) {
    		if (m.getTime() < dates[i].getTime()) {
    			m = dates[i];
    		}
    	}
        return m;
    }
    
    /**
     * 获取当时的年数
     */
    public static int yearOf(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.YEAR);
    }
    
    /**
     * 获取当时的月份
     * @return 月份， 从0（一月）开始
     * @see Calendar#JANUARY
     * @see Calendar#FEBRUARY
     * @see Calendar#MARCH
     * @see Calendar#APRIL
     * @see Calendar#MAY
     * @see Calendar#JUNE
     * @see Calendar#JULY
     * @see Calendar#AUGUST
     * @see Calendar#SEPTEMBER
     * @see Calendar#OCTOBER
     * @see Calendar#NOVEMBER
     * @see Calendar#DECEMBER
     * @see Calendar#UNDECIMBER
     */
    public static int monthOf(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.MONTH);
    }
    
    /**
     * 当天在当年中所处的天数
     * @return 天数， 从1开始
     */
    public static int dayOfYear(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.DAY_OF_YEAR);
    }
    
    
    /**
     * 当天在当月中所处的天数
     * @return 天数， 从1开始
     */
    public static int dayOfMonth(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.DAY_OF_MONTH);
    }
    
    /**
     * 获取星期
     * @return 星期， 从0（星期日）开始
     * @see Calendar#SUNDAY
     * @see Calendar#MONDAY
     * @see Calendar#TUESDAY
     * @see Calendar#WEDNESDAY
     * @see Calendar#THURSDAY
     * @see Calendar#FRIDAY
     * @see Calendar#SATURDAY
     */
    public static int dayOfWeek(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.DAY_OF_WEEK);
    }
    
    /**
     * 获取一年中的周数
     * @return 周数
     */
    public static int weekOfYear(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.WEEK_OF_YEAR);
    }
    
    /**
     * 获取某一月中的周数
     * @return 周数
     */
    public static int weekOfMonth(Date date) {
    	Calendar cal = getCalendar();
    	cal.setTime(date);
    	return cal.get(Calendar.WEEK_OF_MONTH);
    }
    
    
    
    private static Calendar getCalendar() {
    	Calendar cal = CALENDAR_TL.get();
    	if (cal == null) {
    		cal = Calendar.getInstance();
    		CALENDAR_TL.set(cal);
    	}
    	return cal;
    }
    
    private static DateFormat getFormat(String pattern) {
    	ThreadLocal<DateFormat> local = POOL.get(pattern);
    	if (local == null) {
    		local = new ThreadLocal<>();
    		POOL.put(pattern, local);
    	}
    	DateFormat sdf = local.get();
        if (sdf == null) {
            sdf = new SimpleDateFormat(pattern);
            local.set(sdf);
        }
        return sdf;
    }
    
}
